﻿#include <learn/mesh/mesh.h>

#define FAST_OBJ_IMPLEMENTATION
#include <third/fast_obj.h>

using namespace xi;
using namespace xi::mesh;

void Mesh_Data::read(const char *path, float scale)
{
    fastObjMesh *m = fast_obj_read(path);
    ASSERT_INFO(m != nullptr, "Failed to load obj file: " + std::string(path));

    Mesh_Data mesh;

    // 只保存一个材质信息
    if (m->material_count)
    {
        mesh->material = std::make_shared<Material>();
        mesh->material->name = m->materials[0].name;
        mesh->material->Ka[0] = m->materials[0].Ka[0];
        mesh->material->Ka[1] = m->materials[0].Ka[1];
        mesh->material->Ka[2] = m->materials[0].Ka[2];
        mesh->material->Kd[0] = m->materials[0].Kd[0];
        mesh->material->Kd[1] = m->materials[0].Kd[1];
        mesh->material->Kd[2] = m->materials[0].Kd[2];
        mesh->material->Ks[0] = m->materials[0].Ks[0];
        mesh->material->Ks[1] = m->materials[0].Ks[1];
        mesh->material->Ks[2] = m->materials[0].Ks[2];
        mesh->material->Ke[0] = m->materials[0].Ke[0];
        mesh->material->Ke[1] = m->materials[0].Ke[1];
        mesh->material->Ke[2] = m->materials[0].Ke[2];
        mesh->material->Kt[0] = m->materials[0].Kt[0];
        mesh->material->Kt[1] = m->materials[0].Kt[1];
        mesh->material->Kt[2] = m->materials[0].Kt[2];
        mesh->material->Ns = m->materials[0].Ns;
        mesh->material->Ni = m->materials[0].Ni;
        mesh->material->Tf[0] = m->materials[0].Tf[0];
        mesh->material->Tf[1] = m->materials[0].Tf[1];
        mesh->material->Tf[2] = m->materials[0].Tf[2];
        mesh->material->d = m->materials[0].d;
        mesh->material->illum = m->materials[0].illum;
        mesh->material->map_Ka = m->materials[0].map_Ka.path ? m->materials[0].map_Ka.path : "";
        mesh->material->map_Kd = m->materials[0].map_Kd.path ? m->materials[0].map_Kd.path : "";
        mesh->material->map_Ks = m->materials[0].map_Ks.path ? m->materials[0].map_Ks.path : "";
        mesh->material->map_Ke = m->materials[0].map_Ke.path ? m->materials[0].map_Ke.path : "";
        mesh->material->map_Kt = m->materials[0].map_Kt.path ? m->materials[0].map_Kt.path : "";
        mesh->material->map_Ns = m->materials[0].map_Ns.path ? m->materials[0].map_Ns.path : "";
        mesh->material->map_Ni = m->materials[0].map_Ni.path ? m->materials[0].map_Ni.path : "";
        mesh->material->map_d = m->materials[0].map_d.path ? m->materials[0].map_d.path : "";
        mesh->material->map_bump = m->materials[0].map_bump.path ? m->materials[0].map_bump.path : "";
    }

    // 预先创建顶点，第一个顶点没有用，但是在后面作为点的存在性判断，需要空出来
    mesh->vertices.reserve(m->position_count - 1);
    for (unsigned int i = 1; i < m->position_count; i++)
        mesh->vertices.emplace_back(m->positions[3 * i + 0] * scale, m->positions[3 * i + 1] * scale,
                                    m->positions[3 * i + 2] * scale);
    mesh->normals.resize(mesh->vertices.size());
    mesh->texcoords.resize(mesh->vertices.size());

    // 创建面
    for (unsigned int ii = 0; ii < m->group_count; ii++)
    {
        // 获得组
        const fastObjGroup &grp = m->groups[ii];

        int idx = 0;
        for (unsigned int jj = 0; jj < grp.face_count; jj++)
        {
            unsigned int fv = m->face_vertices[grp.face_offset + jj];

            std::vector<int> fvs;
            for (unsigned int kk = 0; kk < fv; kk++)
            {
                fastObjIndex &mi = m->indices[grp.index_offset + idx];

                // 点坐标，根据 if 条件，mi.p > 0，因此第一个点用不上
                if (mi.p)
                {
                    auto vh = mesh->vertices[mi.p - 1];
                    fvs.push_back(mi.p - 1);

                    if (mi.t)
                        mesh->texcoords[mi.p - 1] = Vec2f{m->texcoords[2 * mi.t + 0], m->texcoords[2 * mi.t + 1]};

                    if (mi.n)
                        mesh->normals[mi.p - 1] =
                            Vec3f{m->normals[3 * mi.n + 0], m->normals[3 * mi.n + 1], m->normals[3 * mi.n + 2]};

                    idx++;
                }
            }

            // 创建三角面
            for (auto f : fvs)
                mesh->indices.push_back(f);
        }
    }

    fast_obj_destroy(m);

    // 自赋值
    *this = mesh;
}